package com.catjiu;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 节点解析到word中
 */
public class Html2WordConvert {
    private Log log = LogFactory.get();
    private XWPFDocument word = new XWPFDocument();
    private Context context = new Context();
    //构建一个转换器
    public static Html2WordConvert builder(){
        return new Html2WordConvert();
    }

    /**
     * html转换成word
     * @param html
     */
    public XWPFDocument convert(String html){
        html = html.replace("&nbsp:"," ");
        html = html.replaceAll("\\p{Cntrl}","");
        Element element = Jsoup.parse(html);
        List<Node> nodes = element.getElementsByTag("body").get(0).childNodesCopy();
        for (Node node : nodes) {
            NodeParser nodeParser = selectParser(node);
            if(nodeParser==null){
                continue;
            }
            nodeParser.parser(null, null);
        }
        return word;
    }

    /**
     * 选择节点处理器
     * @param node
     * @return
     */
    private NodeParser selectParser(Node node){
        if(node instanceof Element){
            switch (((Element) node).tagName()){
                case "p":
                    return new PNodeParser(node);
                case "span":
                    return new SpanNodeParser(node);
                case "a":
                    return new HerfNodeParser(node);
                case "br":
                    return null;
                case "strong":
                    return new StrongNodeParser(node);
                default:
                    log.warn("未知的节点类型{}",((Element) node).tagName());
            }
        }else{
            return  new TextNodeParser(node);
        }
        return null;
    }

    /**
     * 节点解析器
     */
    private abstract class NodeParser{
        //当前节点
        Node node;

        public NodeParser(Node node) {
            this.node = node;
        }

        public Node getNode(){
            return node;
        }
        /**
         * 解析节点
         * @param cpst
         * @param crs
         */
        public abstract void parser(ParagraphStyle cpst, RunStyle crs);
    }
    //strong标签
    private class StrongNodeParser extends SpanNodeParser{
        public StrongNodeParser(Node node) {
            super(node);
        }
        @Override
        public void parser(ParagraphStyle cpst, RunStyle parentCrs) {
            if(parentCrs == null){
                parentCrs = new RunStyle();
            }
            parentCrs.bold = true;
            for (Node child : getNode().childNodesCopy()) {
                NodeParser nodeParser = selectParser(child);
                if(nodeParser!=null){
                    RunStyle crs = BeanUtil.toBean(parentCrs,RunStyle.class);
                    nodeParser.parser(cpst, crs);
                }
            }
        }
    }
    /**
     * 处理纯文本标签
     */
    private class TextNodeParser extends NodeParser{

        public TextNodeParser(Node node) {
            super(node);
        }

        @Override
        public void parser(ParagraphStyle cpst, RunStyle crs) {
            TextNode text = (TextNode) getNode();
            XWPFParagraph paragraph = context.getParagraph();
            XWPFRun run = paragraph.createRun();
            //设置文本
            run.setText(text.text());
            if(crs!=null){
                crs.rend(run);
            }
        }
    }

    /**
     * a标签解析
     */
   private class HerfNodeParser extends NodeParser{

       public HerfNodeParser(Node node) {
           super(node);
       }

       @Override
       public void parser(ParagraphStyle cpst, RunStyle crs) {

       }
   }
    /**
     * 解析P标签
     */
    private class PNodeParser extends NodeParser{
        public PNodeParser(Node node) {
            super(node);
        }

        @Override
        public void parser(ParagraphStyle parentPst, RunStyle crs) {
            XWPFParagraph paragraph = word.createParagraph();
            context.setParagraph(paragraph);
            String style = getNode().attr("style");
            ParagraphStyle pst;
            if(parentPst==null){
                pst = new ParagraphStyle().of(style);
            }else{
                pst = parentPst;
                pst.of(style);
            }
            pst.rendParagraph(paragraph);
            for (Node child : getNode().childNodesCopy()) {
                NodeParser nodeParser = selectParser(child);
                if(nodeParser==null){
                    continue;
                }
                ParagraphStyle cpst = BeanUtil.toBean(pst, ParagraphStyle.class);
                nodeParser.parser(cpst, crs);
            }
        }
    }

    /**
     * 处理span标签
     */
    private class SpanNodeParser extends NodeParser{

        public SpanNodeParser(Node node) {
            super(node);
        }
        @Override
        public void parser(ParagraphStyle cpst, RunStyle parentCrs) {
            Element span = (Element) node;
            String style = span.attr("style");
            RunStyle runStyle;
            if(parentCrs==null){
                runStyle = new RunStyle().of(style);
            }else{
                runStyle = parentCrs.of(style);
            }
            for (Node child : node.childNodesCopy()) {
                NodeParser nodeParser = selectParser(child);
                if(nodeParser!=null){
                    RunStyle crs = BeanUtil.toBean(runStyle,RunStyle.class);
                    nodeParser.parser(cpst,crs);
                }
            }
        }
    }
    /**
     * 上下文
     */
    @Data
    private class Context{
        //当前段落
        XWPFParagraph paragraph;
        //当前的run节点
        XWPFRun run;
    }

    /**
     * 样式
     */
    public class Style{
        /**
         *  将style转换成map
         * @param style eg: text-align: center; margin-top: 10px; margin-bottom: 10px; line-height: 1.5em; text-indent: 2em;
         * @return
         */
        public Map<String,String> parseStyle(String style){
            Map<String,String> styleMap = new HashMap<>();
            if(StrUtil.isEmpty(style)){
                return styleMap;
            }
            String[] arr = style.split(";");
            for (String s : arr) {
                styleMap.put(s.split(":")[0].trim(),s.split(":")[1].trim());
            }
            return  styleMap;
        }
    }

    /**
     * span的标签落在run上面
     *  style="border: 1px solid #000000; text-decoration: line-through;"
     */
    private class RunStyle extends  Style{
        //外边框
        private String border;
        //文本修饰：下划线（underline）、上划线（overline）、删除线（line-through）
        private String textDecoration;
        //加粗
        private Boolean bold;
        public RunStyle of(String style){
            Map<String, String> styleMap = parseStyle(style);
            border = styleMap.get("border");
            textDecoration = styleMap.get("text-decoration");
            return this;
        }
        public void rend(XWPFRun run){
            //外边框
            if(StrUtil.isNotEmpty(border)){
                CTRPr rpr = run.getCTR().isSetRPr()?run.getCTR().getRPr():run.getCTR().addNewRPr();
                CTBorder bdr = rpr.addNewBdr();
                bdr.setVal(STBorder.SINGLE);
                bdr.setSz(BigInteger.valueOf(4));
                bdr.setSpace(BigInteger.valueOf(0));
                bdr.setColor("auto");
            }
            //文本修饰
            if(StrUtil.isNotEmpty(textDecoration)){
                CTRPr rpr = run.getCTR().isSetRPr()?run.getCTR().getRPr():run.getCTR().addNewRPr();
                if(textDecoration.equals("underline")){
                    rpr.addNewU();
                }else if(textDecoration.equals("line-through")){
                    rpr.addNewStrike();
                }
            }
            //加粗
            if(bold!=null && bold){
                CTRPr rpr = run.getCTR().isSetRPr()?run.getCTR().getRPr():run.getCTR().addNewRPr();
                rpr.addNewB();
                rpr.addNewBCs();
            }
        }
    }
    /**
     * p标签样式，段落样式
     */
    @Data
    @NoArgsConstructor
    public class ParagraphStyle extends Style{
        //首行缩进
        private String textIndent;
        //居中
        private String textAlign;
        //对应段前距离
        private String marginTop;
        //断后距离
        private String marginBottom;
        //行高
        private String lineHeight;
        /**
         * 设置样式
         * @param style
         * @return
         */
        public ParagraphStyle of(String style){
            Map<String, String> styleMap = parseStyle(style);
            if(styleMap.containsKey("text-align")){
                textAlign = styleMap.get("text-align");
            }
            if(styleMap.containsKey("text-indent")){
                textIndent = styleMap.get("text-indent");
            }
            if(styleMap.containsKey("margin-top")){
                marginTop = styleMap.get("margin-top");
            }
            if(styleMap.containsKey("margin-bottom")){
                marginBottom = styleMap.get("margin-bottom");
            }
            if(styleMap.containsKey("line-height")){
                lineHeight = styleMap.get("line-height");
            }
            return this;
        }

        /**
         * 渲染段落样式
         * @param paragraph
         */
        public void rendParagraph(XWPFParagraph paragraph){
            CTP ctp = paragraph.getCTP();
            CTPPr ppr = ctp.addNewPPr();
            //首行缩进
            if(StrUtil.isNotEmpty(textIndent)){
                CTInd ctInd = ppr.isSetInd()?ppr.getInd():ppr.addNewInd();
                ctInd.setLeftChars(BigInteger.valueOf(100));
                ctInd.setLeft(210);
            }
            //对齐方式
            if(StrUtil.isNotEmpty(textAlign)){
                if(textAlign.equals("right")){
                    ppr.addNewJc().setVal(STJc.RIGHT);
                }else if( textAlign.equals("center")){
                    ppr.addNewJc().setVal(STJc.CENTER);
                }else{
                    ppr.addNewJc().setVal(STJc.LEFT);
                }
            }
            //行高
            if(StrUtil.isNotEmpty(lineHeight)){
                CTSpacing ctSpacing = ppr.isSetSpacing()?ppr.getSpacing():ppr.addNewSpacing();
                ctSpacing.setLine(UnitCalc.html2WordLineHeight(lineHeight));
                ctSpacing.setLineRule(STLineSpacingRule.AUTO);
            }
            //段前
            if(StrUtil.isNotEmpty(marginTop)){
                CTSpacing ctSpacing = ppr.isSetSpacing()?ppr.getSpacing():ppr.addNewSpacing();
                ctSpacing.setBefore(240);
            }
            //段后
            if(StrUtil.isNotEmpty(marginBottom)){
                CTSpacing ctSpacing = ppr.isSetSpacing()?ppr.getSpacing():ppr.addNewSpacing();
                ctSpacing.setAfter(240);
            }
        }
    }

    /**
     * 单位计算器
     */
    private static class UnitCalc{
        /**
         * html中的行高转word中的行高
         *  word中单行行高240 ，html中单行行高1.0em
         * @param lineEm
         * @return
         */
        public static BigInteger html2WordLineHeight(String lineEm){
            String numStr = lineEm.replace("em", "");
            return BigInteger.valueOf((long) (240 * Double.parseDouble(numStr)));
        }
    }
}
